<template>
  <ElFormItem
    v-show="!viewHide"
    ref="formItem"
    :prop="config.key"
    :label="config.label"
    :class="cls"
    v-bind="wrapProps"
  >
    <slot v-if="config.render === undefined" v-bind="mainPropsFinal">{{
      mainPropsFinal.modelValue
    }}</slot>
    <component :is="config.render" v-else v-bind="mainPropsFinal">
      <template v-if="children !== undefined" #default>
        <component
          :is="children"
          v-if="typeof children === 'function'"
        ></component>
        <template v-else>
          {{ children }}
        </template>
      </template>
    </component>
  </ElFormItem>
</template>
<script lang="ts" setup>
import type { FormMainItemProps, FormItemSubmitType } from "./types";
import type { FormItemInstance } from "element-plus";
import {
  generateFormData,
  getVModelSugar,
  getPlaceholder,
  getEnterSubmit,
  getBlurSubmit,
  getChangeSubmit,
} from "./utils";
import _omit from "lodash/omit";
import type { WatchStopHandle } from "vue";

const props = defineProps<FormMainItemProps>();

const emits = defineEmits<{
  (
    event: "viewHideChange",
    info: {
      config: FormMainItemProps["config"];
      status: boolean;
    },
  ): void;
  (e: "submit", type: FormItemSubmitType): void;
}>();

defineOptions({
  name: "FormItem",
});

const formItem = shallowRef<FormItemInstance>();

/** 被关联配置初始 */
const beLinkConfigInit = computed(() => {
  const {
    config: { beLink },
  } = props;
  return beLink ? (Array.isArray(beLink) ? beLink : [beLink]) : [];
});

/** 被关联配置[有效的] */
const beLinkConfig = computed(() => {
  return beLinkConfigInit.value.filter((item) => item.key !== props.config.key);
});

/** 有被关联配置 */
const haveBeLink = computed(() => {
  return beLinkConfig.value.length > 0;
});

const currentValue = computed(() => {
  return props.data[props.config.key];
});

const scope = computed(() => {
  return {
    data: props.data,
    config: props.config,
  };
});

const wrapProps = computed(() => {
  const { config } = props;
  return typeof config.wrapProps === "function"
    ? config.wrapProps(scope.value)
    : config.wrapProps || {};
});

const mainProps = computed(() => {
  const { config } = props;
  return typeof config.props === "function"
    ? config.props(scope.value)
    : config.props || {};
});

/** 回车提交 */
const enterHandler = () => {
  emits("submit", "enter");
};

/** 失焦提交 */
const blurHandler = () => {
  emits("submit", "blur");
};

/** 变更提交 */
const changeHandler = () => {
  emits("submit", "change");
};

/** 更新联动值 */
const updateLinkValue = (n: any, attachInfo?: any) => {
  beLinkConfig.value.forEach((item) => {
    const { key, getValue } = item;
    const value = getValue(n, props.data[key], attachInfo);
    // eslint-disable-next-line vue/no-mutating-props
    props.data[key] = getValue(n, value);
  });
};

const mainPropsPlaceholder = computed(() => {
  return getPlaceholder(props.config) || {};
});

const mainPropsEvents = computed(() => {
  return {
    ...(getEnterSubmit(props.config, enterHandler) || {}),
    ...(getBlurSubmit(props.config, blurHandler) || {}),
    ...(getChangeSubmit(props.config, changeHandler) || {}),
  };
});

const mainPropsOmitChildren = computed(() => {
  return _omit(mainProps.value, ["children"]);
});

const mainPropsVModel = computed(() => {
  return getVModelSugar({
    modelValueKey: "modelValue",
    data: props.data,
    config: props.config,
  });
});

const mainPropsBeLink = computed(() => {
  return haveBeLink.value
    ? {
        onTriggerChangeLink: updateLinkValue,
      }
    : {};
});

const mainPropsFinal = computed(() => {
  return {
    ...mainPropsPlaceholder.value,
    ...mainPropsEvents.value,
    ...mainPropsOmitChildren.value,
    ...mainPropsVModel.value,
    ...mainPropsBeLink.value,
  };
});
const children = computed(() => {
  const { children } = mainProps.value;
  return children;
});

/**
 * 视图是否隐藏
 */
const viewHide = computed(() => {
  const { config } = props;
  const { hide } = config;
  return typeof hide === "boolean"
    ? hide
    : hide?.({
        ...scope.value,
        getInitValue: () => {
          const res = generateFormData([config]);
          return res[config.key];
        },
      }) ?? false;
});

const cls = computed(() => {
  return `form-item-${props.config.key}`;
});

watch(
  viewHide,
  (n) => {
    emits("viewHideChange", {
      config: props.config,
      status: n,
    });
    if (n) {
      formItem.value?.clearValidate();
    }
  },
  {
    immediate: true,
  },
);

let unwatchCurrentValue: WatchStopHandle;

watch(
  haveBeLink,
  (n) => {
    if (n) {
      unwatchCurrentValue = watch(
        currentValue,
        (n) => {
          updateLinkValue(n);
        },
        {
          immediate: true,
        },
      );
    } else {
      unwatchCurrentValue?.();
    }
  },
  {
    immediate: true,
  },
);
</script>
